package com.yl.request.common.glaencrypt;

import com.yl.request.common.glaencrypt.enums.CryptLevel;
import com.yl.request.common.glaencrypt.enums.NetworkType;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.*;
import java.util.Arrays;

public class DxpCrypt {
    private static String defaultPublicKey;
    private static String defaultPrivateKey;
    private static CryptLevel defaultCrypLevel;
    private static NetworkType defaultNetworkType;
    private static String defaultSysCode;

    static {
        Provider provider = Security.getProvider("BC");
        if (provider == null) {
            Security.addProvider(new BouncyCastleProvider());
        }

    }

    public DxpCrypt() {
    }

    public static String getDefaultPublicKey() {
        return defaultPublicKey;
    }

    public static void setDefaultPublicKey(String defaultPublicKey) {
        DxpCrypt.defaultPublicKey = defaultPublicKey;
    }

    public static String getDefaultPrivateKey() {
        return defaultPrivateKey;
    }

    public static void setDefaultPrivateKey(String defaultPrivateKey) {
        DxpCrypt.defaultPrivateKey = defaultPrivateKey;
    }

    public static CryptLevel getDefaultCrypLevel() {
        return defaultCrypLevel;
    }

    public static void setDefaultCrypLevel(CryptLevel defaultCrypLevel) {
        DxpCrypt.defaultCrypLevel = defaultCrypLevel;
    }

    public static NetworkType getDefaultNetworkType() {
        return defaultNetworkType;
    }

    public static void setDefaultNetworkType(NetworkType defaultNetworkType) {
        DxpCrypt.defaultNetworkType = defaultNetworkType;
    }

    public static String getDefaultSysCode() {
        return defaultSysCode;
    }

    public static void setDefaultSysCode(String defaultSysCode) {
        DxpCrypt.defaultSysCode = defaultSysCode;
    }

    public static Cipher generateCipher(int mode, String key) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
        byte[] keyHash = sm3Hash(key);
        byte[] keyBytes = Arrays.copyOfRange(keyHash, 8, 24);
        return generateCipher(mode, keyBytes);
    }

    public static Cipher generateCipher(int mode, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
        Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", "BC");
        Key sm4Key = new SecretKeySpec(keyBytes, "SM4");
        cipher.init(mode, sm4Key);
        return cipher;
    }

    public static byte[] sm3Hash(String data) {
        byte[] dataBytes = data.getBytes();
        SM3Digest digest = new SM3Digest();
        digest.update(dataBytes, 0, dataBytes.length);
        byte[] hash = new byte[digest.getDigestSize()];
        digest.doFinal(hash, 0);
        return hash;
    }

    public static DxpDecryptInputStream decryptInputStreamWapper(File file) throws Exception {
        return decryptInputStreamWapper(file, defaultCrypLevel, defaultPrivateKey);
    }

    public static DxpDecryptInputStream decryptInputStreamWapper(File file, CryptLevel cryptLevel, String sm2Privatekey) throws Exception {
        return decryptInputStreamWapper(new FileInputStream(file), cryptLevel, sm2Privatekey);
    }

    public static DxpDecryptInputStream decryptInputStreamWapper(InputStream input) throws Exception {
        return decryptInputStreamWapper(input, defaultCrypLevel, defaultPrivateKey);
    }

    public static DxpDecryptInputStream decryptInputStreamWapper(InputStream input, CryptLevel cryptLevel, String sm2Privatekey) throws Exception {
        DxpDecryptInputStream dxpInputStream = new DxpDecryptInputStream(input, cryptLevel, sm2Privatekey);
        return dxpInputStream;
    }

    public static DxpEncryptInputStream encryptInputStreamWapper(File file) throws Exception {
        return encryptInputStreamWapper(file, defaultPublicKey);
    }

    public static DxpEncryptInputStream encryptInputStreamWapper(File file, String sm2PublicKey) throws Exception {
        return encryptInputStreamWapper(file, defaultCrypLevel, defaultNetworkType, defaultSysCode, sm2PublicKey);
    }

    public static DxpEncryptInputStream encryptInputStreamWapper(File file, CryptLevel cryptLevel, NetworkType networkType, String sysCode, String sm2PublicKey) throws Exception {
        return encryptInputStreamWapper(new FileInputStream(file), file.length(), cryptLevel, networkType, sysCode, sm2PublicKey);
    }

    public static DxpEncryptInputStream encryptInputStreamWapper(InputStream input, long length) throws Exception {
        return encryptInputStreamWapper(input, length, defaultCrypLevel, defaultNetworkType, defaultSysCode, defaultPublicKey);
    }

    public static DxpEncryptInputStream encryptInputStreamWapper(InputStream input, long length, CryptLevel cryptLevel, NetworkType networkType, String sysCode, String sm2PublicKey) throws Exception {
        if (length == 0L) {
            throw new IllegalArgumentException("InputStream length is 0");
        } else if (cryptLevel == null) {
            throw new IllegalArgumentException("cryptLevel is null,please configure");
        } else if (networkType == null) {
            throw new IllegalArgumentException("networkType is null,please configure");
        } else if (sysCode != null && sysCode.length() == 2) {
            if (sm2PublicKey != null && !"".equals(sm2PublicKey)) {
                DxpEncryptInputStream dxpInputStream = new DxpEncryptInputStream(input, length, cryptLevel, networkType, sysCode, sm2PublicKey);
                return dxpInputStream;
            } else {
                throw new IllegalArgumentException("sm2PublicKey is empty,please configure");
            }
        } else {
            throw new IllegalArgumentException("sysCode length not equal to 2");
        }
    }
}
